/*******************************************************************************
 * Copyright (c) 2012 Philip Douglas.
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 ******************************************************************************/
package net.pdouglas.revision;

/**
 * TODO
 * 
 * @author Philip
 * 
 */
public class Timer {
    // Mode Constants
    public static final int MODE_OFF = 0;
    public static final int MODE_REVISE = 1;
    public static final int MODE_RELAX = 2;
    public static final int MODE_PAUSED = 3;
    
    /* Fields */
    // Relax/revise ratio
    private float               mRatio = 0.5f;
    // Tick size
    private float               mTickSize = 1.0f;
    // Current revise time
    private int                 mReviseTime = 0;
    // TODO time limits
    // Current relax time
    private float               mRelaxTime = 0.0f;
    // Current mode
    private volatile int        mMode = Timer.MODE_OFF;
    // Alarm time
    private int                 mAlarmTime = -1;
    // Alarm object
    private TimerEventHandler   mEventHandler;
    
    public Timer(TimerEventHandler eventHandler) {
        super();
        this.pause();
        this.mEventHandler = eventHandler;
    }
    
    public Timer(TimerEventHandler eventHandler, int revise, float relax) {
        this(eventHandler);
    	this.mReviseTime = revise;
    	this.mRelaxTime = relax;
    }
    
    public synchronized void tick() {
        // Do the tick first, then deal with other events because we don't want 
        // the interface to be out of date when events trigger
        switch (this.mMode){
        case Timer.MODE_OFF:
            return;
        case Timer.MODE_REVISE:
            mReviseTime++;
            mRelaxTime += mRatio;
            break;
        case Timer.MODE_RELAX:
            mRelaxTime--;
            break;
        default:
            break;
        }
        if(mAlarmTime >= 0) mAlarmTime--;
        mEventHandler.handleEvent(new TimerEventTick(
                mReviseTime, 
                floor(mRelaxTime),
                mAlarmTime));
        
        // Other Events
        if(mMode == Timer.MODE_RELAX){
            if(mRelaxTime == 0){
                mEventHandler.handleEvent(
                        new TimerEventZeroRelax());
            }else if(mRelaxTime < 0){
                mEventHandler.handleEvent(new TimerEventNegativeRelax(floor(mRelaxTime)));
            }
        }
        if(mAlarmTime == 0){
            mEventHandler.handleEvent(new TimerEventAlarm());
            }
    }

    public void setMode(int mode) {
        switch (mode) {
            case Timer.MODE_OFF: stop();
                                 break;
            case Timer.MODE_REVISE: revise();
                                    break;
            case Timer.MODE_RELAX: relax();
                                   break;
            case Timer.MODE_PAUSED: pause();
                                    break;
        }
    }

    public int getMode() {
        return mMode;
    }
    
    /** Pauses the timer. */
    public void pause() {
        this.mMode = Timer.MODE_PAUSED;
    }
    
    /** Puts the timer in revise mode, where both counters count up. */
    public void revise() {
        this.mMode = Timer.MODE_REVISE;
    }
    
    /** Puts the timer in relax mode, where the relax counter counts down. */
    public void relax() {
        this.mMode = Timer.MODE_RELAX;
    }
    
    /** Sets the alarm to the given value and starts it counting down. */
    public void setAlarm(int i) {
    	//TODO
    }
    
    /** Ends the timer. Values in counters will be lost.. */
    public void stop(){
        this.mMode = Timer.MODE_OFF;
    }

    public void setTickSize(float mTickSize) {
        this.mTickSize = mTickSize;
    }

    public float getTickSize() {
        return mTickSize;
    }

    public void setRatio(float mRatio) {
        this.mRatio = mRatio;
    }

    public float getRatio() {
        return mRatio;
    }

    public int getReviseTime() {
        return mReviseTime;
    }
    
    public int getRelaxTime() {
        return floor(mRelaxTime);
    }
    
    public int getAlarmTime() {
    	// TODO Return 0 if -1?
    	return mAlarmTime;
    }

    private int floor(float f) {
        return (int) Math.floor((double) f);
    }

    public void reset() {
        mReviseTime = 0;
        mRelaxTime  = 0.0f;
        mAlarmTime  = -1;
    }
}
